package me.zhengjie.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.req.ParamReq;
import okhttp3.*;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @Author xc_wang@probe-group.com
 * @Date 2021/7/19
 * @Description OK HTTP工具实现
 */
@Slf4j
public class HttpUtils {
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    private static volatile OkHttpClient OK_HTTP_CLIENT = null;
    private static volatile Semaphore SEMAPHORE = null;
    private static volatile HttpUtils instance = null;
    private String defaultUrl;

    /**
     * 初始化
     * @param defaultUrl
     */
    public static void init(String defaultUrl){
        if(instance == null){
            synchronized (HttpUtils.class){
                if(instance == null){
                    instance = new HttpUtils(defaultUrl);
                }
            }
        }
    }

    public static HttpUtils build(){
        return new HttpUtils("http://47.103.26.129:40000");
    }

    /**
     * 初始化okHttpClient，并且允许https访问
     */
    private HttpUtils(String defaultUrl) {
        this.defaultUrl = defaultUrl;
        TrustManager[] trustManagers = buildTrustManagers();
        OK_HTTP_CLIENT = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
                .hostnameVerifier((hostName, session) -> true)
                .retryOnConnectionFailure(true)
                .build();
    }

    /**
     * 用于异步请求时，控制访问线程数，返回结果
     *
     * @return
     */
    private static Semaphore getSemaphoreInstance() {
        //只能1个线程同时访问
        synchronized (HttpUtils.class) {
            if (SEMAPHORE == null) {
                SEMAPHORE = new Semaphore(0);
            }
        }
        return SEMAPHORE;
    }

    /**
     * 同步请求
     *
     * @return
     */
    public String sync() {
        //setHeader(request);
        try {
            Response response = OK_HTTP_CLIENT.newCall(requestBuilder().build()).execute();
            if(response.body() != null){
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("请求失败",e);
        }
        return null;
    }

    /**
     * 异步请求，有返回值
     */
    public String async() {
        StringBuilder buffer = new StringBuilder("");
        //setHeader(request);
        OK_HTTP_CLIENT.newCall(requestBuilder().build()).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //buffer.append("请求出错：").append(e.getMessage());
                log.error("请求出错", e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                assert response.body() != null;
                buffer.append(response.body().string());
                getSemaphoreInstance().release();
            }
        });
        try {
            getSemaphoreInstance().acquire();
        } catch (InterruptedException e) {
            log.error("异步请求n异常", e);
        }
        return buffer.toString();
    }

    /**
     * 异步请求，带有接口回调
     *
     * @param callBack
     */
    public void async(ICallBack callBack) {
        //setHeader(request);
        OK_HTTP_CLIENT.newCall(requestBuilder().build()).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callBack.onFailure(call, e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                assert response.body() != null;
                callBack.onSuccessful(call, response.body().string());
            }
        });
    }

    /**
     * 生成安全套接字工厂，用于https请求的证书跳过
     *
     * @return
     */
    private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
           log.error("createSSLSocketFactory error", e);
        }
        return ssfFactory;
    }

    private static TrustManager[] buildTrustManagers() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
    }

    /**
     * 自定义一个接口回调
     */
    public interface ICallBack {

        void onSuccessful(Call call, String data);

        void onFailure(Call call, String errorMsg);

    }

    private Request.Builder requestBuilder(){
        Request.Builder request = new Request.Builder();
        request.addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
        return request;
    }


    public String post(ParamReq param){
        String requestJSON = JSONObject.toJSONString(param);
        log.debug("REQ {}",requestJSON);
        RequestBody body = RequestBody.create(MEDIA_TYPE, requestJSON);
        Request req = requestBuilder().url(defaultUrl).post(body).build();
        Call call = this.OK_HTTP_CLIENT.newCall(req);//同步请求
        String ret = null;
        try {
            Response response = call.execute();
            if(response.code()==200){
                ret = response.body().string();
                //log.debug("RSP {} {}",param.getMethod(), ret);
            }else{
                log.error("RSP {} {}",param.getMethod(), response.code());
            }
        } catch (Exception e){
            log.error(param.getMethod()+" Error, {}", e.getMessage());
        }
        return ret;
    }
}

